package cn.yhjz.live.trans;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.FrameGrabber;
import org.springframework.util.StringUtils;

import java.io.IOException;

import static org.bytedeco.ffmpeg.global.avcodec.av_packet_unref;

@Data
@Slf4j
public class TransPushJavaCV implements ITransPush{
    private String sourceUrl;
    private static final String TARGET_URL = "http://localhost:8888/live/push";
    private FFmpegFrameGrabber grabber;
    private FFmpegFrameRecorder recorder;

    private boolean willStop = false;

    /**
     * 开始拉取、转码、推流
     */
    public void start() {
        if (StringUtils.isEmpty(this.sourceUrl)) {
            throw new RuntimeException("请提供sourceUrl");
        }

        new Thread(() -> {
            log.info("start push");
            int hash = this.sourceUrl.hashCode();
            String sourceHash = Integer.toString(hash);
            log.info("拉取{},sourceId:{}", this.sourceUrl, sourceHash);
            try {
                grabber = FFmpegFrameGrabber.createDefault(this.sourceUrl);
                grabber.setOption("rtsp_transport", "tcp");
                grabber.start();
                recorder = new FFmpegFrameRecorder(TARGET_URL + "/" + sourceHash, grabber.getImageWidth(),
                        grabber.getImageHeight(), grabber.getAudioChannels());

                // 降低编码延时
//                recorder.setVideoOption("tune", "zerolatency");
//                recorder.setVideoOption("crf", "25");
                // 提升编码速度
//                recorder.setVideoOption("preset", "ultrafast");
//                recorder.setInterleaved(true);

                recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG1VIDEO);
                // 录制的视频格式 flv(rtmp格式) h264(udp格式) mpegts(未压缩的udp) rawvideo
                recorder.setFormat("mpegts");
                recorder.start(grabber.getFormatContext());

                grabber.flush();
                long err_index = 0;
                for (int no_frame_index = 0; no_frame_index < 10 || err_index > 1; ) {
                    AVPacket pkt;
                    if (willStop) {
                        break;
                    }
                    try {
                        pkt = grabber.grabPacket();
                        if (pkt == null || pkt.size() <= 0 || pkt.data() == null) {
                            //空包记录次数跳过
                            no_frame_index++;
                            continue;
                        }
                        //不需要编码频帧推出去
                        err_index += (recorder.recordPacket(pkt) ? 0 : 1);//如果失败err_index自增1
                        av_packet_unref(pkt);
                    } catch (IOException e) {//推流失败
                        err_index++;
                    }
                }
            } catch (FFmpegFrameGrabber.Exception exception) {
                exception.printStackTrace();
            } catch (FFmpegFrameRecorder.Exception exception) {
                exception.printStackTrace();
            } catch (FrameGrabber.Exception e) {
                e.printStackTrace();
            }
            log.info("push finish");
        }).start();
    }

    public void stop() {
        log.info("stop push");
        try {
            this.recorder.stop();
            this.grabber.stop();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        this.willStop = true;
        log.info("finish stop");
    }
}
